﻿using NDK.AcqDevice;
using DALSA.SaperaLT.SapClassBasic;
using HalconDotNet;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Threading;

namespace NDK.Module.Model
{
    public class DalsaLineCameraController : INotifyPropertyChanged
    {
        private string _name = "";
        private int cardIndex = 0;
        private int resourceIndex = 0;
        private string camLinkName = "";
        private string configPath = "";

        private AutoResetEvent eventWait = new AutoResetEvent(false);//采集完成信号
        private AutoResetEvent eventWait_Bitmap = new AutoResetEvent(false);//采集完成信号
        private AutoResetEvent eventWait_BitmapList = new AutoResetEvent(false);//采集完成信号
        private HImage _image = new HImage();
        private Bitmap _bitimage;
        private List<Bitmap> _bitmapList;
        public CamSight camSight;

        //public SapBuffer buffEx;
        SapAcqToBuf toBuff;
        SapBufferWithTrash buffTrash;
        SapAcquisition acq;
        SapAcqDevice dev;
        string sServer;//卡名称

        //相机当前状态
        public bool isConnect = false;
        public event PropertyChangedEventHandler PropertyChanged;

        private string status = string.Empty;
        public string Status
        {
            get
            {
                return status;
            }
            private set
            {
                status = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(value.ToString()));
            }
        }

        public int CardIndex
        {
            get
            {
                return cardIndex;
            }

            set
            {
                cardIndex = value;
            }
        }

        public int ResourceIndex
        {
            get
            {
                return resourceIndex;
            }

            set
            {
                resourceIndex = value;
            }
        }

        public string CamLinkName
        {
            get
            {
                return camLinkName;
            }

            set
            {
                camLinkName = value;
            }
        }

        public string ConfigPath
        {
            get
            {
                return configPath;
            }

            set
            {
                configPath = value;
            }
        }

        public string Name
        {
            get
            {
                return _name;
            }

            set
            {
                _name = value;
            }
        }

        public bool InitCard()
        {
            try
            {
                //获取卡数量
                int nServer = SapManager.GetServerCount();
                if (nServer <= 0) { Status = "Can't Find Camera Card\n"; return false; }

                sServer = SapManager.GetServerName(CardIndex);//获取卡名称

                int nResource = SapManager.GetResourceCount(sServer, SapManager.ResourceType.Acq);
                if (nResource < 2) { Status = $"Find {nResource} Camera of 2\n"; }
            }
            catch (Exception ex)
            {
                isConnect = false;
                Status = "Exception Message:" + ex.Message + Environment.NewLine + "StackTrace:" + ex.StackTrace;
                return false;
            }
            return true;

        }

        public bool InitCamera()
        {
            try
            {
                if (!File.Exists(ConfigPath))
                {
                    isConnect = false;
                    Status = "Exception Message: 未找到配置文件";
                    return false;
                }
                SapLocation serverLocation = new SapLocation(sServer, ResourceIndex);
                if (!SapManager.IsResourceAvailable(serverLocation, SapManager.ResourceType.Acq))
                {
                    Status = "Exception Message: Not Available - Resource in Use";
                    return false;
                }
                acq = new SapAcquisition(serverLocation, ConfigPath);
                buffTrash = new SapBufferWithTrash(2, acq, SapBuffer.MemoryType.ScatterGather);
                toBuff = new SapAcqToBuf(acq, buffTrash);

                bool bAcq = acq.Create();
                bool bBuff = buffTrash.Create();


                bool bToBuff = toBuff.Create();
                SapLocation sapLoc = new SapLocation(CamLinkName, 0);
                dev = new SapAcqDevice(sapLoc, false);
                dev.Create();

                toBuff.XferNotify += new SapXferNotifyHandler(xfer_XferNotify);
                toBuff.XferNotifyContext = this;

                isConnect = true;
            }
            catch (Exception ex)
            {
                isConnect = false;
                Status = "Exception Message:" + ex.Message + Environment.NewLine + "StackTrace:" + ex.StackTrace;
                return false;
            }
            return true;
        }
        /// <summary>
        /// 连接指定位置索引的相机
        /// </summary>
        /// <param name="camIndex">相机索引 </param>
        /// <param name="configPath">配置文件路径</param>
        /// <returns></returns>
        public bool Connect()
        {
            bool isSuccess = InitCard();
            isSuccess &= InitCamera();

            return isSuccess;
        }
        /// <summary>
        /// 对外提供采集到的当前图片
        /// </summary>
        /// <param name="timeout">超时时间（ms）</param>
        /// <returns></returns>
        public HImage Image(int timeout = 50)
        {
            eventWait.WaitOne(timeout);
            this.Abort();

            if (_image?.IsInitialized() == true)
            {
                return _image.CopyImage();
            }
            else
            {
                return null;
            }
        }

        public Bitmap BitImage(int timeout = 5000)
        {
            eventWait_Bitmap.WaitOne(timeout);

            this.Abort();
            return _bitimage;
        }

        public List<Bitmap> BitImageList(int timeout = 5000)
        {
            eventWait_BitmapList.WaitOne(timeout);

            this.Abort();
            return _bitmapList;
        }
        public int GetLineRate()
        {
            var Line_Rate_Feature = 0;
            var b = dev.GetFeatureValue("measureLineRate", out Line_Rate_Feature);
            //b = dev.GetFeatureValue("Measured Line Rate", out Line_Rate_Feature);
            if (b) return Line_Rate_Feature;
            else return -1;
        }

        public void Snap()
        {
            try
            {
                eventWait.Reset();
                _image = null;

                eventWait_Bitmap.Reset();
                _bitimage = null;
                eventWait_BitmapList.Reset();
                _bitmapList = null;

                toBuff.Snap();
            }
            catch (Exception ex)
            {
                Status = "Exception Message:" + ex.Message + Environment.NewLine + "StackTrace:" + ex.StackTrace;

            }
        }

        public void Abort()
        {
            try
            {
                toBuff.Abort();
            }
            catch (Exception ex)
            {
                Status = "Exception Message:" + ex.Message + Environment.NewLine + "StackTrace:" + ex.StackTrace;
            }
        }
        public void SetCut(CamSight cs)
        {
            camSight = cs;
        }

        public string SetPara(double exposure, double gain)
        {
            string msg = string.Empty;
            try
            {
                var cnt = 0;
            SET:
                bool isSetOK = true;
                bool bExp = true;
                bool bGain = true;
                double pExp = 0;
                double pGain = 0;
                if (exposure != -1)
                    bExp = dev.SetFeatureValue("ExposureTime", exposure);
                if (gain != -1)
                    bGain = dev.SetFeatureValue("Gain", gain);
                if (!bExp)
                {
                    isSetOK = false;
                    msg += "SetFeatureValue（ExposureTime）Error ";
                }
                if (!bGain)
                {
                    isSetOK = false;
                    msg += "SetFeatureValue（Gain）Error ";
                } 

                dev.GetFeatureValue("ExposureTime", out pExp);
                dev.GetFeatureValue("Gain", out pGain);

                if (!bExp || !bGain || pExp != exposure || pGain != gain)
                {
                    msg += $"Dalsa设置相机参数失败,重试次数:{cnt},pSet:{exposure},{gain},pGet:{pExp},{pGain}";
                    cnt++;
                    if (cnt <= 3) goto SET;
                    else
                    {
                        isSetOK = false;
                        msg += $"Error,Dalsa设置相机参数失败";
                    }

                }

                if (exposure >= 10000)
                    throw new Exception($"ExposureTime={exposure}");
                if (gain >= 20)
                    throw new Exception($"gain={gain}");

                if (isSetOK)
                    msg = "OK";
                return msg;
            }
            catch (Exception ex)
            {
                msg = $"Error,{ex.ToString()}";
                return msg;
            }
        }

        public double getExposure()
        {
            double pExp = 0;
            dev.GetFeatureValue("ExposureTime", out pExp);

            return pExp;
        }

        public double getGain()
        {
            double pGain = 0;
            dev.GetFeatureValue("Gain", out pGain);

            return pGain;
        }
        void xfer_XferNotify(object sender, SapXferNotifyEventArgs argsNotify)
        {
            IntPtr ptr;
            buffTrash.GetAddress(out ptr);

            UpdateFrameRate();
            //HObject obj;
            //HOperatorSet.GenImage1(out obj, "byte", buffTrash.Width, buffTrash.Height, ptr);
            //_image = new HImage(obj);
            eventWait.Set();

            Bitmap bit = BufferToBitmap(ptr, buffTrash.Width, buffTrash.Height);

            CamSight sight = new CamSight();
            sight.FlipX = camSight.FlipX;
            _bitimage = camHandle(bit, sight);

            List<Bitmap> bitList = BufferToBitmapList(ptr, buffTrash.Width, buffTrash.Height);
            _bitmapList = new List<Bitmap>();
            foreach (var bitmap in bitList)
            {
                Bitmap bitTemp = camHandle(bitmap, camSight);
                _bitmapList.Add(bitTemp);
            }

            eventWait_Bitmap.Set();
            eventWait_BitmapList.Set();

            Status = $"Receive Image OK,Width:{buffTrash.Width},Height:{buffTrash.Height}";

        }

        private void UpdateFrameRate()
        {
            if (toBuff.UpdateFrameRateStatistics())
            {
                float framerate = 0.0f;
                SapXferFrameRateInfo stats = toBuff.FrameRateStatistics;

                if (stats.IsLiveFrameRateAvailable)
                    framerate = stats.BufferFrameRate;
                else if (stats.IsLiveFrameRateAvailable && !stats.IsLiveFrameRateStalled)
                    framerate = stats.LiveFrameRate;

                buffTrash.FrameRate = framerate;

            }
        }


        public Bitmap BufferToBitmap(IntPtr ImageP, int width, int height)
        {
            #region BufferToBitmap

            //获取Hobject变量的指针
            //HOperatorSet.GetImagePointer3(ho_Image,out R,out G,out B,out type,out width,out height);
            //
            Bitmap imagee = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
            BitmapData imageData = imagee.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);

            //
            //建立缓存数据
            byte[] bitdata = new byte[width * width];
            int hang = height;
            int lie = width;
            //将画板填充颜色
            ColorPalette pal = imagee.Palette;
            for (int i = 0; i <= 255; i++)
            {
                pal.Entries[i] = Color.FromArgb(255, i, i, i);
            }
            imagee.Palette = pal;
            //通过中间数组将HObject转化为Image
            for (int Num = 0; Num < lie; Num++)
            {
                IntPtr inptr = ImageP + ((Num) * hang);
                IntPtr imptr = imageData.Scan0 + (Num * hang);

                System.Runtime.InteropServices.Marshal.Copy(inptr, bitdata, 0, hang);
                System.Runtime.InteropServices.Marshal.Copy(bitdata, 0, imptr, hang);

            }
            imagee.UnlockBits(imageData);

            return imagee;

            #endregion
        }

        public List<Bitmap> BufferToBitmapList(IntPtr ImageP, int width, int height)
        {
            List<Bitmap> imgList = new List<Bitmap>();
            try
            {
                int ScanHeight = height;
                int ImgHeight = height / 3;
                #region BufferToBitmap

                Bitmap imagee1 = new Bitmap(width, ImgHeight, PixelFormat.Format8bppIndexed);
                Bitmap imagee2 = new Bitmap(width, ImgHeight, PixelFormat.Format8bppIndexed);
                Bitmap imagee3 = new Bitmap(width, ImgHeight, PixelFormat.Format8bppIndexed);
                BitmapData imageData1 = imagee1.LockBits(new Rectangle(0, 0, width, ImgHeight), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
                BitmapData imageData2 = imagee2.LockBits(new Rectangle(0, 0, width, ImgHeight), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
                BitmapData imageData3 = imagee3.LockBits(new Rectangle(0, 0, width, ImgHeight), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);

                //建立缓存数据
                byte[] bitdata = new byte[width * ImgHeight];

                //将画板填充颜色
                ColorPalette pal = imagee1.Palette;
                pal = imagee2.Palette;
                pal = imagee3.Palette;
                for (int i = 0; i <= 255; i++)
                {
                    pal.Entries[i] = Color.FromArgb(i, i, i);
                }
                imagee1.Palette = pal;
                imagee2.Palette = pal;
                imagee3.Palette = pal;

                Double grayTotal1 = 0;
                Double grayTotal2 = 0;
                Double grayTotal3 = 0;
                //通过中间数组将HObject转化为Image
                for (int Num = 0; Num < height; Num++)
                {
                    if (Num % 3 == 1)
                    {
                        int hang = Num / 3;
                        IntPtr inptr = ImageP + ((Num) * width);
                        IntPtr imptr = imageData1.Scan0 + (hang * width);

                        System.Runtime.InteropServices.Marshal.Copy(inptr, bitdata, 0, width);
                        for (int i = 0; i < width; i++)
                        {
                            grayTotal1 += bitdata[i];
                        }
                        System.Runtime.InteropServices.Marshal.Copy(bitdata, 0, imptr, width);
                    }
                    else if (Num % 3 == 2)
                    {
                        int hang = Num / 3;
                        IntPtr inptr = ImageP + ((Num) * width);
                        IntPtr imptr = imageData2.Scan0 + (hang * width);


                        System.Runtime.InteropServices.Marshal.Copy(inptr, bitdata, 0, width);
                        for (int i = 0; i < width; i++)
                        {
                            grayTotal2 += bitdata[i];
                        }
                        System.Runtime.InteropServices.Marshal.Copy(bitdata, 0, imptr, width);
                    }
                    else if (Num % 3 == 0)
                    {
                        int hang = Num / 3;
                        IntPtr inptr = ImageP + ((Num) * width);
                        IntPtr imptr = imageData3.Scan0 + (hang * width);

                        System.Runtime.InteropServices.Marshal.Copy(inptr, bitdata, 0, width);
                        for (int i = 0; i < width; i++)
                        {
                            grayTotal3 += bitdata[i];
                        }
                        System.Runtime.InteropServices.Marshal.Copy(bitdata, 0, imptr, width);
                    }
                }

                imagee1.UnlockBits(imageData1);
                imagee2.UnlockBits(imageData2);
                imagee3.UnlockBits(imageData3);

                List<double> grayList = new List<double>();
                grayList.Add(grayTotal1);
                grayList.Add(grayTotal2);
                grayList.Add(grayTotal3);
                ;
                int dirtyIndex = grayList.FindIndex(x => x == grayList.Min());
                Console.WriteLine(Name + ":" + dirtyIndex);
                if (dirtyIndex == 0)
                {
                    imgList.Add(imagee2);
                    imgList.Add(imagee3);
                    imgList.Add(imagee1);
                }
                else if (dirtyIndex == 1)
                {
                    imgList.Add(imagee3);
                    imgList.Add(imagee1);
                    imgList.Add(imagee2);
                }
                else if (dirtyIndex == 2)
                {
                    imgList.Add(imagee1);
                    imgList.Add(imagee2);
                    imgList.Add(imagee3);
                }




                #endregion
            }
            catch (Exception ex)
            {

            }
            return imgList;
        }

        public void Close()
        {
            try
            {
                isConnect = false;
                ////销毁缓存
                //toBuff?.Destroy();
                //buffTrash?.Destroy();
                //dev.Destroy();
                //acq.Destroy();

                if (toBuff != null && toBuff.Initialized)
                    toBuff.Destroy();
                if (buffTrash != null && buffTrash.Initialized)
                    buffTrash.Destroy();
                if (acq != null && acq.Initialized)
                    acq.Destroy();

                if (toBuff != null)
                { toBuff.Dispose(); toBuff = null; }

                if (buffTrash != null)
                { buffTrash.Dispose(); buffTrash = null; }
                if (acq != null)
                { acq.Dispose(); acq = null; }
            }
            catch (Exception ex)
            {
                Status = "Exception Message:" + ex.Message + Environment.NewLine + "StackTrace:" + ex.StackTrace;
            }
        }
        ~DalsaLineCameraController()
        {
            this.Close();
        }

        private static Bitmap camHandle(Bitmap bit, CamSight camSight)
        {
            if (camSight == null)
                return bit;
            bit = RotateFlip(bit, camSight);
            // bit = crop(bit, camSight);
            return bit;
        }
        private static Bitmap RotateFlip(Bitmap bit, CamSight camSight)
        {
            if (camSight.Angle == 90 && camSight.FlipX) bit.RotateFlip(RotateFlipType.Rotate90FlipX);
            if (camSight.Angle == 90 && !camSight.FlipX) bit.RotateFlip(RotateFlipType.Rotate90FlipNone);
            else if (camSight.Angle == 180 && camSight.FlipX) bit.RotateFlip(RotateFlipType.Rotate180FlipX);
            else if (camSight.Angle == 180 && !camSight.FlipX) bit.RotateFlip(RotateFlipType.Rotate180FlipNone);
            else if (camSight.Angle == 270 && camSight.FlipX) bit.RotateFlip(RotateFlipType.Rotate270FlipX);
            else if (camSight.Angle == 270 && !camSight.FlipX) bit.RotateFlip(RotateFlipType.Rotate270FlipNone);
            else if (camSight.Angle == 0 && camSight.FlipX) bit.RotateFlip(RotateFlipType.RotateNoneFlipX);
            else if (camSight.Angle == 0 && !camSight.FlipX) bit.RotateFlip(RotateFlipType.RotateNoneFlipNone);

            return bit;
        }
        private static Bitmap crop(Bitmap bit, CamSight cs)
        {
            if (cs.CutX < 0) cs.CutX = 0;
            if (cs.CutY < 0) cs.CutY = 0;
            if (cs.CutW > bit.Width - cs.CutX) cs.CutW = bit.Width - cs.CutX;
            if (cs.CutH > bit.Height - cs.CutY) cs.CutH = bit.Height - cs.CutY;

            if (cs.CutX > bit.Width || cs.CutY > bit.Height) return bit;
            if (cs.CutW == 0 || cs.CutH == 0) return bit;

            Rectangle cropRect = new Rectangle(cs.CutX, cs.CutY, cs.CutW, cs.CutH);
            Bitmap target = new Bitmap(cropRect.Width, cropRect.Height);
            using (Graphics g = Graphics.FromImage(target))
            {
                g.DrawImage(bit, new Rectangle(0, 0, target.Width, target.Height), cropRect, GraphicsUnit.Pixel);
            }
            return target;
        }

    }
}

